import { isPlainObject, isSameData } from "@/utils/base";
import { Add, RemoveCircleOutline } from "@mui/icons-material";
import {
  Dropdown,
  Input,
  DropdownMenu,
  DropdownItem,
  SelectItem,
  Select,
  Switch,
  Textarea,
  Button,
  Divider,
} from "@nextui-org/react";
import React, { useEffect, useMemo, useRef, useState } from "react";

type Props = {
  type: FieldType;
  field?: string;
  initValue?: any;
  level?: number;
  onChange: (value: any) => void;
};

const ConfigValue: React.FC<Props> = ({ type, onChange, initValue }) => {
  // const [field, setField] = useState('')
  const initValMap = useMemo(() => {
    const valueType = typeof initValue;
    const isObj = isPlainObject(initValue);
    const isArr = Array.isArray(initValue);
    const isNum = !isNaN(parseInt(initValue as string));

    return {
      string: valueType === "string" && initValue ? initValue : "",
      number: isNum && initValue ? Number(initValue) : 0,
      boolean: valueType === "boolean" && initValue ? initValue : false,
      object: isObj && initValue ? initValue : {},
      array: isArr && initValue ? initValue : [],
      json: valueType === "string" && initValue ? initValue : "{}",
    };
  }, [initValue]);

  const getInitialValueByType = (type: FieldType) => {
    return initValMap[type];
  };

  const getInitialValueByCurrentType = () => {
    return initValMap[type];
  };

  const [value, setValue] = useState<any>(getInitialValueByCurrentType);
  // console.log("🚀 ~ ===configValue comp===value:", value);
  const [entries, setEntries] = useState<any[]>([]);
  const [list, setList] = useState<any[]>([]);

  const setupList = (list: any) => {
    // console.log("🚀 ~ setupList ~ list:", list);
    setList(list);
  };

  const appendNewListItem = () => {
    setupList([...list, { id: list.length, type: "string", value: "" }]);
  };

  const insertListItem = (data: any, isBatch?: boolean, isReset?: boolean) => {
    const newList = isBatch ? data : [data];
    setupList((list: any) => {
      return [
        ...(isReset ? [] : list),
        ...newList.map((value: any, i: number) => ({
          id: list.length + i,
          type: typeof value,
          value,
        })),
      ];
    });
  };

  const removeListItem = (index: number) => {
    const newList = [...list];
    newList.splice(index, 1);
    setupList(newList);
  };

  const insertListItemRef = useRef(insertListItem);
  insertListItemRef.current = insertListItem;

  const setupEntries = (entries: any) => {
    console.log("🚀 ~ setupEntries ~ entries:", entries);
    setEntries(entries);
  };

  const updateListItemValue = (index: number, value: any) => {
    const newList = [...list];
    const oldItem = list[index];
    if (!isSameData(oldItem.value, value)) {
      newList[index] = Object.assign(oldItem, { value });
      setupList(newList);
    }
  };

  const updateListItemType = (index: number, type: any) => {
    const newList = [...list];
    const oldItem = list[index];
    const defValue = getInitialValueByType(type);
    if (!isSameData(oldItem.value, defValue)) {
      newList[index] = Object.assign(oldItem, { type, value: defValue });
      setupList(newList);
    }
  };

  const initValueRef = useRef(initValue);

  //   useEffect(() => {
  //     console.log("🚀 ~ useEffect ~ initValueRef.current:", initValueRef.current);
  //     if (initValueRef.current == null && initValue) {
  //       setValue(initValue);
  //     } else if (initValueRef.current != null && initValue == null) {
  //       setValue(initValMap[type]);
  //     }

  //     initValueRef.current = initValue;
  //   }, [initValue, type, initValMap]);

  if (type === "array") {
    !list.length && !Array.isArray(initValue) && appendNewListItem();
  }

  useEffect(() => {
    // sync init value to list
    if (
      typeRef.current === "array" &&
      Array.isArray(initValue) &&
      initValue.length
    ) {
      insertListItemRef.current(initValue, true, true);
    }
  }, [initValue]);

  useEffect(() => {
    if (type !== "array") return;
    const items = list
      .filter((item) => item.value != null)
      .map((item) => item.value);
    const newValue = items.length ? items : [];
    setValueAndNotify(newValue);
  }, [list, type]);

  const appendNewEntry = () => {
    setupEntries([
      ...entries,
      { id: entries.length, field: "", type: "string", value: "" },
    ]);
  };

  const appendNewEntryRef = useRef(appendNewEntry);
  appendNewEntryRef.current = appendNewEntry;

  const insertEntry = (field: string, value: any) => {
    setupEntries((entries: any) => {
      return [
        ...entries,
        { id: entries.length, field, type: typeof value, value },
      ];
    });
  };

  const insertEntryRef = useRef(insertEntry);
  insertEntryRef.current = insertEntry;

  const removeEntry = (index: number) => {
    const newEntries = [...entries];
    newEntries.splice(index, 1);
    if (!newEntries.length) {
      newEntries.push({
        id: 0,
        field: "",
        type: "string",
        value: "",
      });
    }
    setupEntries(newEntries);
  };

  const updateEntry = (
    index: number,
    field: "field" | "type" | "value",
    value: any
  ) => {
    const newEntries = [...entries];
    const oldEntry = entries[index];
    const rtype = field === "type" ? value : oldEntry.type;
    const rfield = field === "field" ? value : oldEntry.field;
    const rvalue = field === "value" ? value : oldEntry.value;

    if (["object", "array"].includes(oldEntry.type)) {
      const oldData = { [oldEntry.field]: oldEntry.value };
      const newData = { [rfield]: rvalue };
      if (isSameData(oldData, newData)) return;
    }

    newEntries[index] = { ...newEntries[index], type: rtype, [field]: value };

    setupEntries(newEntries);
  };

  useEffect(() => {
    if (type !== "object") return;
    const items = entries
      .filter((entry) => entry.field)
      .map((entry) => [entry.field, entry.value]);
    const newValue = items.length ? Object.fromEntries(items) : {};
    setValueAndNotify(newValue);
  }, [entries, type]);

  const handleNotAnyEntry = () => {
    if (type !== "object") return;
    const initVal = initValue;

    if (!entries.length) {
      const hasValidInitValue =
        initVal && isPlainObject(initVal) && Object.keys(initVal).length;
      if (!hasValidInitValue) {
        appendNewEntryRef.current();
      } else {
        for (const [key, value] of Object.entries(initVal)) {
          // console.log("🚀 ~ key, value:", key, value);

          insertEntryRef.current(key, value);
        }
      }
    }
  };

  handleNotAnyEntry();

  // setTimeout(handleNotAnyEntry, 200);

  const typeRef = useRef(type);
  typeRef.current = type;
  const entriesRef = useRef(entries);
  entriesRef.current = entries;

  useEffect(() => {
    // in case popup different modal for editing obj config
    // reset the entries according the init value
    if (
      typeRef.current === "object" &&
      isPlainObject(initValue) &&
      Object.keys(initValue).length
    ) {
      const items = [];
      for (const [key, value] of Object.entries(initValue)) {
        // if value is all numbers, think it as number type
        const numRe = /^[0-9]+$/;
        items.push({
          id: items.length,
          field: key,
          type: numRe.test(value as string) ? "number" : typeof value,
          value,
        });
      }

      if (!isSameData(entriesRef.current, items)) {
        setupEntries(items);
      }
    }
  }, [initValue]);

  const onChangeRef = useRef<(value: any) => void>(onChange);
  onChangeRef.current = onChange;

  //   make it more explicitly
  //   useEffect(() => {
  //     onChangeRef.current(value);
  //   }, [value]);

  const setValueAndNotify = (val: any) => {
    setValue(val);
    onChangeRef.current(val);
  };

  useEffect(() => {
    if (initValueRef.current == null) {
      // notify default value to parent comp
      setValueAndNotify(initValMap[type]);
    } else {
      setValue(initValMap[type]);
    }
  }, [initValMap, type]);

  const typeList = ["string", "number", "boolean", "object", "array", "json"];

  return (
    <div className="config-value">
      <div className="control-line control-value-line">
        {type === "string" && (
          <Input
            label="Value"
            labelPlacement="outside"
            placeholder="Enter value"
            isRequired
            value={value}
            onValueChange={setValueAndNotify}
          />
        )}

        {type === "number" && (
          <Input
            label="Value"
            type="number"
            placeholder="Enter value"
            labelPlacement="outside"
            value={value}
            onValueChange={setValueAndNotify}
          />
        )}

        {type === "boolean" && (
          <Switch isSelected={value} onValueChange={setValueAndNotify}>
            {value ? "True" : "False"}
          </Switch>
        )}

        {type === "json" && (
          <Textarea
            minRows={3}
            label={"Value"}
            value={value}
            labelPlacement="outside"
            placeholder="Enter JSON value"
            onValueChange={setValueAndNotify}
          ></Textarea>
        )}

        {type === "object" && (
          <div className="config-value-object pl-8">
            <div className="text-right pr-2">
              <Button
                size="sm"
                isIconOnly
                variant="flat"
                color="success"
                className="rounded-full"
                onClick={appendNewEntry}
              >
                <Add />
              </Button>
            </div>
            {entries.map((entry, index) => {
              return (
                <React.Fragment key={index}>
                  {index > 0 && <Divider className="my-6" />}

                  <div className={"obj-config-item p-2 relative "}>
                    <RemoveCircleOutline
                      className="text-danger-500 hover:text-danger-300 cursor-pointer absolute right-[-30px] top-[30px] m-2"
                      onClick={() => removeEntry(index)}
                    />
                    <div className="obj-control-line mb-10">
                      <Input
                        label="Field"
                        labelPlacement="outside"
                        placeholder="Enter field name"
                        isRequired
                        value={entry.field}
                        onValueChange={(val) =>
                          updateEntry(index, "field", val)
                        }
                      />
                    </div>
                    <div className="obj-control-line mb-10">
                      <Select
                        label="Type"
                        labelPlacement="outside"
                        isRequired
                        size="md"
                        selectedKeys={[entry.type]}
                        onChange={(e) =>
                          updateEntry(index, "type", e.target.value)
                        }
                        className="w-full"
                        placeholder="Select value type"
                      >
                        {typeList.map((type) => (
                          <SelectItem key={type} value={type}>
                            {type}
                          </SelectItem>
                        ))}
                      </Select>
                    </div>

                    <div className="obj-control-line">
                      <ConfigValue
                        initValue={entry.value}
                        type={entry.type}
                        onChange={(val) => updateEntry(index, "value", val)}
                      />
                    </div>
                  </div>
                </React.Fragment>
              );
            })}
          </div>
        )}

        {type === "array" && (
          <div className="config-value-array ml-8">
            <div className="text-right pr-2">
              <Button
                size="sm"
                isIconOnly
                variant="flat"
                color="secondary"
                className="rounded-full"
                onClick={appendNewListItem}
              >
                <Add />
              </Button>
            </div>
            {list.map((item, index) => {
              return (
                <React.Fragment key={index}>
                  {index > 0 && <Divider className="mt-6 mb-12" />}
                  <div className="arr-config-item pr-4 relative">
                    <RemoveCircleOutline
                      className="text-warning-500 hover:text-warning-300 cursor-pointer absolute right-[-20px] top-[0px] m-2"
                      onClick={() => removeListItem(index)}
                    />
                    <div className="arr-control-line mb-10">
                      <Select
                        label="Type"
                        labelPlacement="outside"
                        isRequired
                        size="md"
                        selectedKeys={[item.type]}
                        onChange={(e) =>
                          updateListItemType(index, e.target.value)
                        }
                        className="w-full"
                        placeholder="Select value type"
                      >
                        {typeList.map((type) => (
                          <SelectItem key={type} value={type}>
                            {type}
                          </SelectItem>
                        ))}
                      </Select>
                    </div>

                    <div className="arr-control-line">
                      <ConfigValue
                        initValue={item.value}
                        type={item.type}
                        onChange={(val) => updateListItemValue(index, val)}
                      />
                    </div>
                  </div>
                </React.Fragment>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
};

export default ConfigValue;
